package multithread.framework.threadmethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import multithread.framework.annotation.Param;
import multithread.framework.annotation.ThreadAfter;
import multithread.framework.annotation.ThreadAllAfter;
import multithread.framework.annotation.ThreadAllBefore;
import multithread.framework.annotation.ThreadBefore;
import multithread.framework.annotation.ThreadFor;
import multithread.framework.annotation.ThreadGroupAfter;
import multithread.framework.annotation.ThreadGroupBefore;
import multithread.framework.annotation.ThreadGroupFor;
import multithread.framework.annotation.Timeout;
import multithread.framework.utility.Utilities;
/**
 * Parse from annotation
 * @author dailey_dai
 *
 */
public class ThreadMethodParser implements IParser<ThreadMethodContainer> {
	private IParseFilter<Method> parseFilter = null;

	public ThreadMethodParser() {
	}

	public ThreadMethodParser(IParseFilter<Method> parseFilter) {
		setParseFilter(parseFilter);
	}

	public void setParseFilter(IParseFilter<Method> parseFilter) {
		this.parseFilter = parseFilter;
	}

	@Override
	public ThreadMethodContainer parse(Object testcase) {
		ThreadMethodContainer container = ThreadMethodContainer.newInsatnce();
		if (testcase == null)
			return container;
		Class<?> clz = testcase.getClass();
		container.setTestCaseClass(clz);
		// every thread method timeout
		Timeout timeout = clz.getAnnotation(Timeout.class);
		if (timeout != null) {
			container.setThreadMethodTimeout(timeout.value());
		}
		// main parse for thread method
		Method[] methods = clz.getDeclaredMethods();
		for (Method method : methods) {
			if (parseFilter != null && !parseFilter.accept(method))
				continue;
			container.addThreadMethod(convertMethod(method));
		}
		return container;
	}

	/**
	 * convert java.lang.reflect.Method to ThreadMethod, do not care validation
	 * 
	 * @param method
	 * @return ThreadMethod
	 */
	protected ThreadMethod convertMethod(Method method) {
		Annotation[] annotations = method.getAnnotations();
		ThreadMethod threadMethod = new ThreadMethod(method);
		boolean isRunnerMethod = false;
		for (Annotation annotation : annotations) {
			if (annotation instanceof ThreadAllBefore) {
				threadMethod.setThreadAllBefor((ThreadAllBefore) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadAllAfter) {
				threadMethod.setThreadAllAfter((ThreadAllAfter) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadGroupBefore) {
				threadMethod
						.setThreadGroupBefore((ThreadGroupBefore) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadGroupAfter) {
				threadMethod.setThreadGroupAfter((ThreadGroupAfter) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadBefore) {
				threadMethod.setThreadBefor((ThreadBefore) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadAfter) {
				threadMethod.setThreadAfter((ThreadAfter) annotation);
				isRunnerMethod = true;
			}
			if (annotation instanceof ThreadGroupFor) {
				threadMethod.setThreadGroupFor((ThreadGroupFor) annotation);
			}
			if (annotation instanceof ThreadFor) {
				threadMethod.setThreadFor((ThreadFor) annotation);
				isRunnerMethod = true;
			}
			// do not take method parameters expect ThreadFor method
			if (isRunnerMethod) {
				threadMethod.setMethodParamTypes(method.getParameterTypes());
				Object[] params = getMethodParams(method);
				threadMethod.setMethodParams(params);
			}
		}
		return threadMethod;
	}

	/**
	 * get test case method parameters
	 * 
	 * @param method
	 *            java.lang.reflect.Method
	 * @return Object[]
	 */
	protected Object[] getMethodParams(Method method) {
		Annotation[][] annotations = method.getParameterAnnotations();
		Class<?>[] clzs = method.getParameterTypes();
		Object[] objs = new Object[clzs.length];
		for (int i = 0; i < clzs.length; i++) {
			Annotation annotation = annotations[i][0];
			Param paramAnotation = null;
			if (Param.class.isInstance(annotation)) {
				paramAnotation = (Param) annotation;
			}
			Class<?> clz = clzs[i];
			if (clz == String.class && paramAnotation != null) {
				objs[i] = (paramAnotation.value()[0]);
			} else if (clz.isPrimitive() && paramAnotation != null) {
				objs[i] = Utilities.convertToPrimitive(clz,
						paramAnotation.value()[0]);
			} else if (clz.isArray()) {
				objs[i] = Utilities.convertToArray(clz, paramAnotation.value());
			} else {
				objs[i] = null;
			}
		}
		return objs;
	}

}
